/* Copyright 2002 -- Ira R. Forman and Nate Forman */ 
/**
 * This is the base class for the class-to-class transformation framework.
 * The framework is intended to facilitate the creation of programs that
 * take a top-level class and generate a new class object.  This is accomplished
 * by generating a .java file that is dynamically compiled and loaded.
 * 
 * Because the framework dynamically invokes javac,
 * there are many generation errors that are not checked by the
 * transformations, because the compiler checks for them.
 * 
 * NOTE: A cooperative method is one that calls its super method.
 * All methods in this framework that begin with "check" or "generate"
 * are required to be cooperative.
 *
 * NOTE: The interface to createClass requires a qualified class name, from which
 * a class object is obtained using Class.forName.  Because of this,
 * all transformations produced by this framework preclude primitives
 * as inputs.  In addition, nested classes may not be used as input class.
 * The test questions/NestedClassTest shows that forName may not be used to 
 * obtain a class object for a nested class even when its declaring is already loaded.
 * Precluding nested classes is desirable because of the many problematic issues
 * raised by using nested classes as inputs for class-to-class transformations.
 * <p>
 * Naming convention:<ul>
 * <li>    Tranformations:  C2<name>C.java
 * <li>    Constructions:   Null2<name>C.java
 * <li>    Aspects:         C2<name>.java
 * </ul>
 * @see c2c.Args
 */
//start extract C2C
package c2c;
import java.io.*;
import mopex.UQueue;

public abstract class C2C {
//stop extract C2C
/**
 * This prefix is prepended to the inputClassName to create the
 * the outputClassName, if the -output flag is not specified on
 * the command line.
 */
//start extract C2C
    protected String  classNamePrefix;                //#1
//stop extract C2C
/**
 * This is the class object determined by forName for this input
 * class specified on the command line.
 */
//start extract C2C
    protected Class   inputClassObject;               //#1
//stop extract C2C
/**
 * This is the (unqualified) name of the input class specified on command line.
 * If a qualified name is specified on the command line, the qualifier is 
 * stripped to produce the value of this field.
 */
//start extract C2C
    protected String  inputClassName = null;          //#1
//stop extract C2C
/**
 * The name of the class to be produced by the generator.  The default
 * name is the classNamePrefix prepended to the inputClassName.  The default
 * may be changed with the -output flag on the command line.  A side effect of
 * the generator is to produce a .java file names with the outputClassName.
 */
//start extract C2C
    protected String  outputClassName;                //#1
//stop extract C2C
/**
 * The class object produced by the generator. The generator produces .java 
 * file that is dynamically compiled and loaded.  This class object is result
 * of loading that compiled class.
 */
//start extract C2C
    protected Class   outputClassObject;              //#1
//stop extract C2C
/**
 * The name of the package in which the generated class resides.  This field
 * is set with the -package flag on the command line.
 */
//start extract C2C
    protected String  packageName;                    //#1
//stop extract C2C
/**
 * The command line must specify the name of the input class. This name may
 * be fully qualified. That value is stored here.
 */
//start extract C2C
    protected String  qualifiedInputClassName = null; //#1
//stop extract C2C
/**
 * The fully qualified name of the output class.  This may be specified on the
 * command line with the -output option.  If the -package option is specified on
 * the command line, the qualifiedOutputClassName is the package name prepended
 * to the outputClassName.
 */
//start extract C2C
    protected String  qualifiedOutputClassName;       //#1

//stop extract C2C
/**
 * If the command line contains the -abstract flag, this field is set
 * to true.  Subsequent to command line processing, this field may be set
 * to true to force the generator to produce an abstract class.
 */
//start extract C2C
    boolean isAbstract;                                           //#2
    protected final void setAbstract() { isAbstract = true; }     //#2
    protected final boolean isAbstract() { return isAbstract; }   //#2
                                                                  //#2
//stop extract C2C
/**
 * If the command line contains the -final flag, this field is set
 * to true.  Subsequent to command line processing, this field may be set
 * to true to force the generator to produce an final class.
 */
//start extract C2C
    boolean isFinal;                                              //#2
    protected final void setFinal() { isFinal = true; }           //#2
    protected final boolean isFinal() { return isFinal; }         //#2
                                                                  //#2
//stop extract C2C
/**
 * If the command line contains the -interface flag, this field is set
 * to true, which means that an interface is produced instead of a class.
 */
//start extract C2C
    boolean isInterface;                                          //#2
    protected final void setInterface() { isInterface = true; }   //#2
    protected final boolean isInterface() { return isInterface; } //#2
                                                                  //#2
//stop extract C2C
/**
 * If the command line contains the -notpublic flag, this field is set
 * to true.  Subsequent to command line processing, this field may be set
 * to true to force the generator to produce a class that is not public (package visibility).
 * Note that the default of the generator is to produce public classes.
 */
//start extract C2C
    boolean isNotPublic;                                          //#2
    protected final void setNotPublic() { isNotPublic = true; }   //#2
    protected final boolean isNotPublic() { return isNotPublic; } //#2
                                                                  //#2
//stop extract C2C
/**
 * This is the template method that performs the generation of the output class.
 * createClass generates the prefix, calls checkAndProcessArgs, generates a string that
 * is the generated class, writes the string to a .java file, executes the Java compiler
 * on that file, loads the resulting .class file, and returns the resulting class object.
 * A subclass of C2C may implement a "main" that calls createClass.  In this manner,
 * generators that can be executed from command lines (or Makefiles) are created.
 */
//start extract C2C
    public final Class createClass( String[] args ) {

	classNamePrefix = generateClassNamePrefix();              //#3

	Args myArgs = new Args(args);                             //#4
	checkAndProcessArgs( myArgs );                            //#4
	if ( !myArgs.complete() )                                 //#4
	    throw new C2CException( "Usage: unprocessed flags: "  //#4
				    + myArgs.toString( ) );       //#4

	UQueue itQ = generateInterfaces();                        //#5
	UQueue importQ = generateImports();                       //#5

	String aClassString =
	    (packageName==null ? "" : "package " + packageName + ";\n")      //#6
	    + (importQ.isEmpty() ? "" : "import "                            //#6
	                                + importQ.toString(";\nimport ")     //#6
	                                + ";\n")                             //#6
	    + getClassLevelJavadoc()                                         //#6
	    + (isNotPublic?"":"public ")                                     //#6
	    + (isFinal?"final ":"")                                          //#6
	    + (isAbstract?"abstract ":"")                                    //#6
	    + (isInterface?" interface ":" class ") + outputClassName + "\n" //#6
	    + (getSuperclass().equals("") ? "" : "    extends "              //#6
	                                         + getSuperclass()           //#6
                                                 + "\n")                     //#6
	    + (itQ.isEmpty() ? "" : "    implements " + itQ.toString(", ") ) //#6
	    + "{\n//============= F I E L D S ======================\n"      //#6
	    + generateFields()                                               //#6
	    +  "\n//============= C O N S T R U C T O R S ==========\n"      //#6
	    + generateConstructors()                                         //#6
	    +  "\n//============= M E T H O D S ====================\n"      //#6
	    + generateMethods()                                              //#6
	    +  "\n//============= N E S T E D   C L A S S E S ======\n"      //#6
	    + generateNestedClasses()                                        //#6
	    + "}\n";                                                         //#6

	try {
	    FileWriter outputFile                                            //#7
		= new FileWriter( outputClassName + ".java" );               //#7
	    outputFile.write( aClassString );                                //#7
	    outputFile.close();                                              //#7
                                                                             //#7
	    String cp = System.getProperty( "java.class.path" );             //#7
	    Process p =                                                      //#7
		Runtime.getRuntime().exec( "javac -source 1.4 -classpath \"" //#7
	    			           + cp                              //#7
					   + "\" "                           //#7
					   + outputClassName                 //#7
					   + ".java");                       //#7
//stop extract C2C
	    //Process p = Runtime.getRuntime().exec( "javac -source 1.4 " + outputClassName + ".java");
	    //Why is my test requiring the setting of the classpath?

	    // In addition, check that the compilation is going to a place that
	    // the forName below will find!!!

	    if (false)
//start extract C2C
                                                                             //#7
	    p.waitFor();                                                     //#7
//stop extract C2C
	    else { // To be used when waitFor fails to terminate
		int exitValue = -1;  // compilation failure is not -1
		int errStreamAvailable = 0;
		while ( exitValue == -1 ) {
		    Thread.sleep( 10 );
		    try {
			exitValue = p.exitValue();
		    } catch(IllegalThreadStateException e){
			InputStream errStream = p.getErrorStream();
			if ( errStream.available() > 0 
			              && errStream.available() == errStreamAvailable ) {
			    for ( int j = errStream.available(); j > 0; j-- )
				System.out.write( errStream.read() );
			    p.destroy();
			    throw new RuntimeException( "compile failed" );
			} 
			errStreamAvailable = errStream.available();
			exitValue = -1;
		    }
		}
	    }
//start extract C2C

	    if ( p.exitValue() == 0 ) {
		outputClassObject = Class.forName(qualifiedOutputClassName); //#8
	    } else {
		InputStream errStream = p.getErrorStream();
		for ( int j = errStream.available(); j > 0; j-- )
		    System.out.write( errStream.read() );
		throw new C2CException( "compile fails " + p.exitValue() );
	    }
	} catch(Exception e){ throw new C2CException(e); }

	checkPostconditions();                                                //#9

	System.out.println( outputClassName + " compiled and loaded" );

	return outputClassObject;
    }

//stop extract C2C
/**
 * This cooperative method produces the string of command line flags for the transformation.
 * Subclasses of C2C use the methods of the Args class to process the command line.
 */
//start extract C2C
    abstract protected String generateFlags();                              //#10
//stop extract C2C
/**
 * This cooperative method determines the prefix for the class name.
 * The prefix is used to produced a default class name for the generated class.
 * The prefix should be an adjective or adjectival phrase.  For example, "NullObjectFor",
 * "QueueOf", or "SingletonFor".  Note that this allow the output of one generator
 * to be named suitability for use as the input to another generator.
 */
//start extract C2C
    abstract protected String generateClassNamePrefix();                    //#10
//stop extract C2C
/**
 * This cooperative method checks and processes the command line for the transformation.
 * Each subclass of C2C may define additional command line flags, which it processes 
 * in an override of checkAndProcessArgs.
 * Subclasses of C2C use the methods of the Args class to process the command line.
 */
//start extract C2C
    abstract protected void   checkAndProcessArgs( Args args );             //#10
//stop extract C2C
/**
 * This cooperative method produces the import packages for the generated class.
 * One uses the add method of UQueue to add just the name of the package to
 * inserted into an imports statement.
 *
 * An example of a correct implementation of this method is:
 * <p>protected UQueue generateImports() {return super.generatesImports("java.lang.reflect.*");}
 *
 * Note that the only way to get a valid QueueOfSting is to call the super method.
 */
//start extract C2C
    abstract protected UQueue generateImports();                            //#10
//stop extract C2C
/**
 * This method produces the documentation comment that is associated with the 
 * class that is being generated. The return string must be a valid comment.
 * that is it must include the comment begin and comment end markers.
 * The javadoc comments for fields, methods, and constructors are generated
 * with the respective field, method, or constructor.
 */
//start extract C2C
    abstract protected String getClassLevelJavadoc();                       //#10
//stop extract C2C
/**
 * This method produces the name of the superclass for the generated class.
 * Because of single inheritance, there is no need for this method
 * to be cooperative.
 */
//start extract C2C
    abstract protected String getSuperclass();                              //#10
//stop extract C2C
/**
 * This cooperative method produces a queue of interfaces for implements clause
 * the generated class. 
 *
 * An example of a correct implementation of this method is:
 * <p>protected UQueue generateInterfaces() {return super.generatesInterfaces("Cloneable");}
 *
 * Note that the only way to get a valid QueueOfSting is to call the super method.
 */
//start extract C2C
    abstract protected UQueue generateInterfaces();                         //#10
//stop extract C2C
/**
 * This cooperative method produces the field statements for the generated class.
 * The substrings should be syntactically correct field statements.
 * For readability of the generated source code, it is recommended that each field statement
 * be terminated with a newline.
 */
//start extract C2C
    abstract protected String generateFields();                             //#10
//stop extract C2C
/**
 * This cooperative method produces the constructor statements for the generated class.
 * The substrings should be syntactically correct constructor statements.
 * For readability of the generated source code, it is recommended that each constructor statement
 * be terminated with a newline.
 */
//start extract C2C
    abstract protected String generateConstructors();                       //#10
//stop extract C2C
/**
 * This cooperative method produces the method statements for the generated class.
 * The substrings should be syntactically correct method statements.
 * For readability of the generated source code, it is recommended that each method statement
 * be terminated with a newline.
 */
//start extract C2C
    abstract protected String generateMethods();                            //#10
//stop extract C2C
/**
 * This cooperative method produces the nested class statements for the generated class.
 * The substrings should be syntactically correct nested class statements.
 * For readability of the generated source code, it is recommended that each nested class statement
 * be terminated with a newline.
 */
//start extract C2C
    abstract protected String generateNestedClasses();                      //#10
//stop extract C2C
/**
 * After the generated class is loaded, checkPostconditions is run.
 * This allows the generator to use introspective operations to see
 * if the generated class is properly formed.
 */
//start extract C2C
    abstract protected void   checkPostconditions();                        //#10
}
//stop extract C2C
